package ch.unisi.inf.pfii.teamred.pacman.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.Timer;

import ch.unisi.inf.pfii.teamred.pacman.controller.CreatureListener;
import ch.unisi.inf.pfii.teamred.pacman.controller.GameListener;
import ch.unisi.inf.pfii.teamred.pacman.model.Block;
import ch.unisi.inf.pfii.teamred.pacman.model.Board;
import ch.unisi.inf.pfii.teamred.pacman.model.Creature;
import ch.unisi.inf.pfii.teamred.pacman.model.Direction;
import ch.unisi.inf.pfii.teamred.pacman.model.Floor;
import ch.unisi.inf.pfii.teamred.pacman.model.Game;
import ch.unisi.inf.pfii.teamred.pacman.model.Item;
import ch.unisi.inf.pfii.teamred.pacman.model.Wall;

/**
 * This class models the view of the Pacman Game.
 * 
 * @author mark.pruneri@lu.unisi.ch
 * 
 */

public final class GameView extends JPanel {

	private static final int TICKS_PER_SECOND = 200;

	private final Game game;
	private final Board board;

	private final BoardView boardView;
	private final PlayerView playerView;

	private final Timer timer;

	public boolean gameOver;
	public boolean gameWon;

	private boolean gamePaused;
	private boolean gameFirstTime;

	private BufferedImage grayedGameImage;

	public GameView(final Game game) {

		this.game = game;
		board = game.getLevel().getBoard();

		boardView = new BoardView(board);
		playerView = new PlayerView(game.getPlayer());

		setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 10));
		setBackground(Color.BLACK);

		timer = new Timer(TICKS_PER_SECOND, new ActionListener() {
			public final void actionPerformed(final ActionEvent ev) {
				game.step();
				repaint();
			}
		});

		setLayout(new BorderLayout());
		add(boardView, BorderLayout.CENTER);
		add(playerView, BorderLayout.SOUTH);

		game.addGameListener(new GameListener() {

			public void gameOver(Game game) {
				boardView.removeListeners();
				timer.stop();
				boardView.setGrayBoard();
				gameOver = true;

				// Here goes the Highscore handling
				// String name = JOptionPane.showInputDialog("Your name: ");
				// game.getHighscore().addHighscore(name,
				// game.getPlayer().getScore());
			}

			public void gameWon(Game game) {
				boardView.removeListeners();
				timer.stop();
				boardView.setGrayBoard();
				gameWon = true;
				// Here goes the Highscore handling if all levels cleared.

			}

		});

		addKeyListener(new KeyAdapter() {
			public void keyPressed(final KeyEvent ev) {
				switch (ev.getKeyCode()) {
				case KeyEvent.VK_UP:
					board.getPacman().setCurrentDirection(Direction.UP);
					break;
				case KeyEvent.VK_DOWN:
					board.getPacman().setCurrentDirection(Direction.DOWN);
					break;
				case KeyEvent.VK_LEFT:
					board.getPacman().setCurrentDirection(Direction.LEFT);
					break;
				case KeyEvent.VK_RIGHT:
					board.getPacman().setCurrentDirection(Direction.RIGHT);
					break;
				case KeyEvent.VK_SPACE:
					if (gamePaused) {
						getPacmanFrame().setResizable(true);
						gamePaused = false;
						boardView.addListeners();
						timer.start();
						boardView.setVisible(true);
						repaint();
					} else {
						getPacmanFrame().setResizable(false);
						boardView.setGrayBoard();
						gamePaused = true;
						boardView.removeListeners();
						timer.stop();
						repaint();
					}
					break;
				}
			}
		});

		gameOver = false;
		gameWon = false;
		gameFirstTime = true;
		gamePaused = false;

		setFocusable(true);
		requestFocus();

		setVisible(true);
	}

	public final BoardView getBoardView() {
		return boardView;
	}

	public final PlayerView getPlayerView() {
		return playerView;
	}

	public final Dimension getPreferredSize() {
		return new Dimension(450, 550);
	}

	public final Timer getTimer() {
		return timer;
	}

	private final PacmanFrame getPacmanFrame() {
		return (PacmanFrame) getParent().getParent().getParent().getParent();
	}

	public boolean isGamePaused() {
		return gamePaused;
	}

	public boolean isGameFirstTime() {
		return gameFirstTime;
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);

	}

	/**
	 * This inner class models the view of the board.
	 * 
	 * @author mark.pruneri@lu.unisi.ch
	 * 
	 */

	class BoardView extends JComponent {

		// mutable values to make possible the scaling of the board.
		private int offsetX;
		private int offsetY;
		private int cellSize;

		private final Board board;
		private final SpriteLibrary spriteLibrary;
		private final CreatureListener creatureListener;

		private boolean ghostsVulnerable;
		private boolean ghostsVulnerableNearEnd;

		private BufferedImage grayedGameImage;

		public BoardView(final Board board) {
			this.board = board;
			ghostsVulnerable = false;
			ghostsVulnerableNearEnd = false;
			spriteLibrary = SpriteLibrary.getInstance();

			creatureListener = new CreatureListener() {
				public final void creatureChangedPosition(
						final Creature creature) {

				}

				public final void creatureChangedDirection(
						final Creature creature) {

				}

				public final void creatureSetDead(final Creature creature) {

				}

				public final void creatureSetAlive(final Creature creature) {

				}

				public final void creatureSetVulnerable(final Creature creature) {

				}

				public final void creatureSetInvulnerable(
						final Creature creature) {
					ghostsVulnerable = true;
					ghostsVulnerableNearEnd = false;
				}

				public final void creatureTimerIsRunningOut(
						final Creature creature) {
					ghostsVulnerableNearEnd = true;
				}

				public final void creatureTimerTimedOut(final Creature creature) {
					ghostsVulnerable = false;
					ghostsVulnerableNearEnd = false;
				}
			};

		}

		public final void paintComponent(final Graphics g) {
			super.paintComponent(g);

			// update cell size.
			cellSize = Math.min(getWidth() / (board.getWidth()), getHeight()
					/ board.getHeight());

			// update board (x,y) offset in panel.
			offsetY = (getWidth() - board.getWidth() * cellSize) / 2;
			offsetX = (getHeight() - board.getHeight() * cellSize) / 2;

			drawBoardBlocks(g, board.getBlocks());
			drawCreatures(g, board.getCreatures());
			drawMessage(g);

		}

		private final void drawMessage(Graphics g) {
			g.setColor(Color.WHITE);
			g.setFont(new Font("Helvetica", Font.PLAIN, cellSize * 2));

			if (gamePaused || gameOver || gameWon) {
				g.drawImage(grayedGameImage, 0, 0, null);
			}

			if (gamePaused) {
				g.drawString("   PAUSED", getWidth() / 2 - cellSize * 2 * 3,
						getHeight() / 2);
			} else if (gameOver) {
				g.drawString("GAMEOVER!", getWidth() / 2 - cellSize * 2 * 3,
						getHeight() / 2);
				gameOver = false;
			} else if (gameWon) {
				g.drawString(" CLEARED!", getWidth() / 2 - cellSize * 2 * 3,
						getHeight() / 2);
				gameWon = false;
			} else if (gameFirstTime) {
				g.drawString("   PACMAN", getWidth() / 2 - cellSize * 2 * 3,
						getHeight() / 2);
				gameFirstTime = false;
			}
		}

		private final void drawCreatures(final Graphics g,
				final ArrayList<Creature> creatures) {
			for (Creature creature : creatures) {
				if (!creature.isControlledByPlayer() && creature.isVulnerable()
						&& !ghostsVulnerableNearEnd) {
					g.drawImage(spriteLibrary.getImage("vulnerable_on"),
							creature.getCurrentPosition().getColumn()
									* cellSize + offsetY, creature
									.getCurrentPosition().getRow()
									* cellSize + offsetX, cellSize, cellSize,
							this);
				} else if (!creature.isControlledByPlayer()
						&& creature.isVulnerable() && ghostsVulnerableNearEnd) {
					g.drawImage(spriteLibrary
							.getImage("vulnerable_on_near_end"), creature
							.getCurrentPosition().getColumn()
							* cellSize + offsetY, creature.getCurrentPosition()
							.getRow()
							* cellSize + offsetX, cellSize, cellSize, this);
				} else {
					g.drawImage(spriteLibrary.getImage(creature.getName() + "_"
							+ creature.getCurrentDirection().toString()),
							creature.getCurrentPosition().getColumn()
									* cellSize + offsetY, creature
									.getCurrentPosition().getRow()
									* cellSize + offsetX, cellSize, cellSize,
							this);
				}
			}
		}

		private final void drawBoardBlocks(Graphics g, Block[][] blocks) {

			for (int x = 0; x < board.getHeight(); x++) {
				for (int y = 0; y < board.getWidth(); y++) {
					// it's a border
					if (!blocks[y][x].isTraversable()) {
						drawWall(g, ((Wall) blocks[y][x]), y, x);
						// there is an item in this block
					} else if (blocks[y][x].containsItem()) {
						drawItem(g, ((Floor) blocks[y][x]).getItems().get(0),
								y, x);
					}
				}
			}
		}

		private final void drawWall(final Graphics g, final Wall wall,
				final int x, final int y) {
			g.drawImage(spriteLibrary.getImage("border" + wall.getType()), x
					* cellSize + offsetY, y * cellSize + offsetX, cellSize,
					cellSize, this);
		}

		private final void drawItem(final Graphics g, final Item item,
				final int x, final int y) {
			g
					.drawImage(spriteLibrary.getImage(item.getName()), x
							* cellSize + offsetY, y * cellSize + offsetX,
							cellSize, cellSize, this);
		}

		public final void addListeners() {
			for (final Creature creature : board.getCreatures()) {
				creature.addCreatureListener(creatureListener);
			}
		}

		public final void removeListeners() {
			for (final Creature creature : board.getCreatures()) {
				creature.removeCreatureListener(creatureListener);
			}
		}

		/**
		 * This method will take a grayscale screenshot of the board.
		 */
		public final void setGrayBoard() {
			grayedGameImage = new BufferedImage(getWidth(), getHeight(),
					BufferedImage.TYPE_BYTE_GRAY);
			paintComponent(grayedGameImage.createGraphics());
		}

	}

}
